热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

故障分析|MySQL5.7使用临时表导致数据库Crash

在MySQL5.7.30主从读写分离环境下,从库在某天出现了MySQ

作者:雷文霆

爱可生华东交付服务部  DBA 成员,主要负责Mysql故障处理及相关技术支持。爱好看书,电影。座右铭,每一个不曾起舞的日子,都是对生命的辜负。

本文来源:原创投稿

* 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


目录

  • 背景信息
  • 故障分析

  • 问题复现

  • 测试日志

  • 测试结论

  • 参考链接

背景信息

在 MySQL5.7.30 主从读写分离环境下,从库在某天出现了 MySQL crash.

系统侧: 监控显示该从库主机的内存和CPU资源使用率在故障前后均正常,磁盘IO有2%的iowait(读写200M/s),说明故障前磁盘存在压力。

服务侧:slow-log 中记录了服务重启前,存在使用了临时表和文件排序的慢 SQL 语句。

Error-log 中记录了服务调用到  btr0btr.cc 文件 的 L2165 行,出现了 err == DB_SUCCESS 报错。

0x7f2dd49d0700  InnoDB: Assertion failure in thread 139834817316608 in file btr0btr.cc line 2165InnoDB: Failing assertion: err == DB_SUCCESSInnoDB: We intentionally generate a memory trap.InnoDB: Submit a detailed bug report to http://bugs.mysql.com.

故障分析

在系统侧排除了磁盘空间不足和内存不足的因素,服务侧怀疑是慢查询和BUG的原因,之后通过" btr0btr.cc"关键字查找到了一个类似的 BUG 。链接如下:

https://bugs.mysql.com/bug.php?id=101154

报告的意思是,MySQL 在执行 btr_insert_on_non_leaf_level_func()函数时,写入临时表会导致带有断言的服务崩溃。

通过查看 btr0btr.cc 文件开头的注释了解到的原因是:

此文件的调用机制是:对b树行操作或记录所做的所有更改。

L2165 行操作内容是:在处理插入到非叶级别的内容时,会检查每个级别的可用空间(需保留2倍索引数高度的页空间),如果在操作之前,叶分裂已经开始,就很难撤销,只能通过崩溃进行前滚。该 BUG 只会在 MySQL5.7 出现 

代码查询:https://github.com/mysql/mysql-server  (通过 Tags 标签选择对应版本)

代码内容:https://github.com/mysql/mysql-server/blob/mysql-5.7.30/storage/innobase/btr/btr0btr.cc#L2165

nserts a data tuple to a tree on a non-leaf level. It is assumedthat mtr holds an x-latch on the tree. */voidbtr_insert_on_non_leaf_level_func()    ---    ut_a(err == DB_SUCCESS);    ---        其中ut_a()https://dev.mysql.com/doc/dev/mysql-server/latest/ut0dbg_8h.html#ae7aed983dfe98ac872b5a9915fa778fa:

检查数据库关于临时表的参数:

innodb_temp_data_file_path ibtmp1:12M:autoextend:max:20G

tmp_table_size 64M 和 max_heap_table_size 64M

注释:实际限制取二者中的较小者。会话级别的参数,对于 innodb_buffer_pool_size 不大且没有用到临时大数据量查询的情况,不建议设置的过大,可能会导致内存溢出的情况。连接数800+,64M为推荐值

internal_tmp_disk_storage_engine InnoDB

注释:用于定义磁盘临时表的存储引擎。超出 InnoDB 行或列限制的磁盘内部临时表的查询会返回 Row size too large 或 Too many columns 错误。解决方法是设置 internal_tmp_disk_storage_engine 为 MYISAM ,我们的 error-log 中无相关报错。初步排查阶段不建议修改

created_tmp_disk_tables 2987733

created_tmp_tables 11049848

注释:当在内存或磁盘上创建内部临时表时,服务器会增加该 Created_tmp_tables
值。在磁盘上创建内部临时表时,服务器会增加该 Created_tmp_disk_tables
值。如果在磁盘上创建了太多内部临时表,请考虑增加tmp_table_size
max_heap_table_size
设置。从早上10点36分到17点产生较多临时表,结合业务繁忙情况,属于正常现象

小结:  通过上面的分析,结合应用架构(无法升级到 MySQL8.0 )。初步阶段是建议先优化 SQL 语句,减少对临时表的使用,降低再次发生的概率。

问题复现

上文提到的 BUG 报告中,有个使用 MySQL Test Run 简称MTR(MySQL官方的自动化测试框架)  的测试用例。
下文将引用此测试用例,进行复现测试。
参数解释:

innodb_limit_optimistic_insert_debug参数可以限制每个B树页面的记录数,在 MySQL 运行过程中动态设置此参数可以导致页分裂。

innodb_limit_optimistic_insert_debug限制每个 B 树页面的记录数。默认值 0 表示不施加限制。仅当使用 CMake选项编译调试支持时,需开启DEBUG选项。# 依赖yum install -y gcc gcc-c++ cmake ncurses ncurses-devel bison openssl openssl-develtar -xvf mysql-boost-5.7.30.tar.gz--编译安装MySQL,因为需要设置innodb_limit_optimistic_insert_debug参数-------tar -xvf mysql-boost-5.7.30.tar.gz# 非BOOST版本的Mysql源码包,需要指定-DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boostcd mysql-5.7.30cmake . -DCMAKE_INSTALL_PREFIX=/tools/mysql-test5.7.30 -DMYSQL_DATADIR=/tools/mysql-test5.7.30/data -DMYSQL_UNIX_ADDR=/tools/mysql-test5.7.30/mysql5.7.30.sock -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DEXTRA_CHARSETS=all -DENABLED_LOCAL_INFILE=1 -DWITH_SSL=system  -DWITH_BOOST=boost -DWITH_DEBUG=1 makemake install# Cmake 编译之后会在DCMAKE_INSTALL_PREFIX目录中生成mysql-test测试框架目录/tools/mysql-test5.7.30/mysql-test/tvim my0420.test cat my0420.test --source include/have_innodb.inc--let $restart_parameters = "restart: --innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:1G --big-tables=1"--source include/restart_mysqld.incSELECT @@innodb_temp_data_file_path;drop function if exists func1;delimiter |;create function func1(x int) returns int deterministic        begin                declare z1, z2 int;                set z1 = x;                set z2 = z1 + 2;                return z2;        end|delimiter ;|create table t1 (a int, b varchar(20));insert into t1 values(1, 'a'), (2, 'b'), (3, 'c'), (3, 'c'), (4, 'c');SET GLOBAL innodb_limit_optimistic_insert_debug=4;--let $i=1while ($i <= 15) {INSERT INTO t1 SELECT * FROM t1;--inc $i}SELECT COUNT(*) FROM t1;SET GLOBAL innodb_limit_optimistic_insert_debug=2;select * from t1 order by func1(a);进行测试,/tools/mysql-test5.7.30/mysql-test./mtr my0420.test其中 select * from t1 order by func1(a); 会使用Using temporary; Using filesort 和 业务SQL的执行计划一致

将 my0420.test 第二行新增--internal_tmp_disk_storage_engine=MYISAM参数后,服务不崩溃。

--let $restart_parameters = "restart: --innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:1G --internal_tmp_disk_storage_engine=MYISAM --big-tables=1"

将 my0420.test 第二行增大为---innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:6G参数后,服务不崩溃。

--let $restart_parameters = "restart: --innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:6G  --big-tables=1"

big_tables
如果启用,服务器将所有临时表存储在磁盘上而不是内存中。这可以防止需要大型临时表的操作的大多数错误,但也会减慢内存表就足够的查询。most The table *
tbl_name* is full
errors forSELECT
operations,如果error-log中出现此报错,说明select 操作使用了大的磁盘临时表,不推荐启用。

(小提示,客户环境中时常会收到某张临时表 #sql_tbl_name is full的告警邮件,需要考虑是否可以优化SQL了)

测试日志

MTR 的执行逻辑为启动一个临时 MySQL 服务,并执行t目录中 my0420.test 文件的内容,执行结果默认会和r目录中的 result 同名文件(也叫标准执行结果文件,一般会在正确的版本中生成)进行对比,用于判断测试文件是否正确。

在mysql-test目录下:./mtr my0420.test  --执行到以下语句时报错--SET GLOBAL innodb_limit_optimistic_insert_debug=2;[100%] main.my0420                              [ fail ]        Test ended at 2022-04-20 20:05:39CURRENT_TEST: main.my0420mysqltest: At line 32: query 'select * from t1 order by func1(a)' failed: 2013: Lost connection to MySQL server during querysafe_process[7080]: Child process: 7081, exit: 1Server [mysqld.1 - pid: 7089, winpid: 7089, exit: 256] failed during test runServer log from this test:

Lost connection ,MySQL 服务已停止。之后打印了 error 信息。错误同样出现在 btr0btr.cc line 2165

Server log from this test:----------SERVER LOG START-----------2022-04-20T12:02:42.082135Z 0 [Note] /tools/mysql-test5.7.30/bin/mysqld (mysqld 5.7.30-debug-log) starting as process 7049 ...2022-04-20T12:02:43.698812Z 0 [Note] /tools/mysql-test5.7.30/bin/mysqld: Shutdown complete2022-04-20T12:02:45.051667Z 0 [Note] /tools/mysql-test5.7.30/bin/mysqld (mysqld 5.7.30-debug-log) starting as process 7090 ...2022-04-20T12:02:45.262573Z 0 [Note] /tools/mysql-test5.7.30/bin/mysqld: ready for connections.Version: '5.7.30-debug-log'  socket: '/tools/mysql-test5.7.30/mysql-test/var/tmp/mysqld.1.sock'  port: 13000  Source distribution2022-04-20 15:05:37 0x7fc298bc4700  InnoDB: Assertion failure in thread 140473762858752 in file btr0btr.cc line 2165InnoDB: Failing assertion: err == DB_SUCCESSInnoDB: We intentionally generate a memory trap.InnoDB: Submit a detailed bug report to http://bugs.mysql.com.InnoDB: If you get repeated assertion failures or crashes, evenInnoDB: immediately after the mysqld startup, there may beInnoDB: corruption in the InnoDB tablespace. Please refer toInnoDB: http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.htmlInnoDB: about forcing recovery.12:05:37 UTC - mysqld got signal 6 ;This could be because you hit a bug. It is also possible that this binaryor one of the libraries it was linked against is corrupt, improperly built,or misconfigured. This error can also be caused by malfunctioning hardware.Attempting to collect some information that could help diagnose the problem.As this is a crash and something is definitely wrong, the informationcollection process might fail.key_buffer_size=1048576read_buffer_size=131072max_used_cOnnections=1max_threads=151thread_count=1connection_count=1It is possible that mysqld could use up tokey_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 61093 K  bytes of memoryHope that's ok; if not, decrease some variables in the equation.Trying to get some variables.Some pointers may be invalid and cause the dump to abort.Query (7fc234005890): select * from t1 order by func1(a)Connection ID (thread ID): 2Status: NOT_KILLED

和客户环境的区别之处:

生产中的报错为 Query(7f3be00479d0) is an invalid pointer. 无效指针,类似磁盘空间不足的报错.

测试中的报错为 Query (7fc234005890): select * from t1 order by func1(a).

测试环境的堆栈信息:

https://github.com/mysql/mysql-server/blob/mysql-5.7.30/storage/innobase/btr/btr0btr.cc#L2285

(上图中文是翻译的代码注释,可能会有些偏差错误)

检查测试日志文件 error-log,默认会在当前目录生成var目录,其中包含 my.cnf 文件

/tools/mysql-test5.7.30/mysql-test/var/log/mysqld.1.err

Trying to get some variables.Some pointers may be invalid and cause the dump to abort.Query (7fc234005890): select * from t1 order by func1(a)Connection ID (thread ID): 2Status: NOT_KILLEDThe manual page at http://dev.mysql.com/doc/mysql/en/crashing.html containsinformation that should help you find out what is causing the crash.Writing a core filesafe_process[7089]: Child process: 7090, killed by signal: 6

关于编译安装 MySQL 的补充:
Cmake 编译之后会在 DCMAKE_INSTALL_PREFIX 目录中生成 mysql-test 测试框架目录,
不需要以下步骤(以下步骤在不需要 DEBUG 调试时使用)
1.此包是mysql-test-5.7.30-linux-glibc2.12-x86_64.tar.gz
mv mysql-5.7.30-linux-glibc2.12-x86_64/ tools/mysql-test
2.将mysql安装包目录下的文件与mtr目录合并,mysql安装包目录下为basedir
cp -r /data/mysql/base/5.7.30 /tools/mysql-test
(插播生活日记,make命令大约需要执行1个小时,刚好18:27,又恰逢隔离,起身洗锅炸厨房了嗷)

测试结论

此 BUG 可能会出现在 MySQL5.7 版本中

1.测试中验证了数据库参数 innodb_temp_data_file_path 增大max_file_size后不会发生服务崩溃,如果业务 SQL 无法进行优化时,可以增大此参数,可降低触发崩溃的概率。
2.测试中验证了数据库参数 internal_tmp_disk_storage_engine=MYISAM 时不会发生服务崩溃,默认 INNODB
如果业务无法升级到 8.0 时,可以动态调整此参数。
我们建议的变更顺序是:
优化 SQL 语句 -> 增大 innodb_temp_data_file_path 参数的 max_file_size 值 -> 升级到 MySQL 8.0(使用会话临时表空间) -> 修改 internal_tmp_disk_storage_engine 参数。
其中 internal_tmp_disk_storage_engine 参数,个人不是很理解,是否真的要将默认值 INNODB 更改为 MYISAM 。之后请教同事了解到,"内部临时表不会被复制,不会有并发访问,是可以考虑使用 MYISAM 的"
再次感谢嗷。

参考链接

BUG报告:

https://bugs.mysql.com/bug.php?id=101154
https://jira.percona.com/browse/PS-7318?focusedCommentId=268045&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-268045

编译安装及参数说明:

https://blog.csdn.net/iteye_621/article/details/81959655
https://baijiahao.baidu.com/s?id=1725289345179642059&wfr=spider&for=pc

https://dev.mysql.com/doc/refman/5.7/en/source-configuration-options.html#cmake-general-options

MTR 文档:

https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_MYSQLTEST_FRAMEWORK_COMPONENTS.html

数据库参数:

https://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html#statvar_Created_tmp_tables

https://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html#statvar_Created_tmp_disk_tables

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_tmp_table_size

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_heap_table_size

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_big_tables

https://dev.mysql.com/doc/refman/5.7/en/select.html


本文关键字#临时表# #编译#  #MTR的使#

文章推荐:

故障分析 | MySQL 使用 Mysqldump 备份导入数据导致主从异常

新特性解读 | 来聊聊 MySQL8.0 的 json 模式校验

技术分享 | TiDB 对大事务的简单拆分

技术分享 | MySQL 内部临时表是怎么存放的


关于SQLE

可生开源社区的 SQLE 是一款面向数据库使用者和管理者,支持多场景审核,支持标准化上线流程,原生支持 MySQL 审核且数据库类型可扩展的 SQL 审核工具。

SQLE 获取
类型地址
版本库https://github.com/actiontech/sqle
文档https://actiontech.github.io/sqle-docs-cn/
发布信息https://github.com/actiontech/sqle/releases
数据审核插件开发文档https://actiontech.github.io/sqle-docs-cn/3.modules/3.7_auditplugin/auditplugin_development.html

更多关于 SQLE 的信息和交流,请加入官方QQ交流群:637150065...



推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
author-avatar
瞄瞄摩卡李流
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有